using System;
using System.Collections.Generic;

namespace FrameWork
{
    public partial class ObjectPoolModule
    {
        public class ObjectPool<T> : ObjectPoolBase where T : ObjectBase
        {
            private Dictionary<string, List<Object<T>>> _objects;

            private Dictionary<object, Object<T>> _objectMap;

            /// <summary>
            /// 可以释放的物体
            /// </summary>
            private List<T> _canReleaseObjects;

            /// <summary>
            /// 要被释放的物体
            /// </summary>
            private List<T> _toReleaseObjects;

            /// <summary>
            /// 多次获取
            /// </summary>
            private bool _allowMultiSpawn;

            /// <summary>
            /// 自动释放间隔
            /// </summary>
            private float _autoReleaseInterval;

            /// <summary>
            /// 容量
            /// </summary>
            private int _capacity;

            /// <summary>
            /// 周期时间
            /// </summary>
            private float _expireTime;

            /// <summary>
            /// 自动释放时间
            /// </summary>
            private float _autoReleaseTime;

            public ObjectPool(string name, bool allowMultiSpawn, float autoReleaseInterval,
                int capacity, float expireTime) : base(name)
            {
                _objectMap = new Dictionary<object, Object<T>>();
                _objects = new Dictionary<string, List<Object<T>>>();
                _canReleaseObjects = new List<T>();
                _toReleaseObjects = new List<T>();
                _allowMultiSpawn = allowMultiSpawn;
                _autoReleaseInterval = autoReleaseInterval;
                _capacity = capacity;
                _expireTime = expireTime;
            }

            public override Type ObjectType => typeof(T);

            public override int Count => _objectMap.Count;

            public override int CanReleaseCount => _canReleaseObjects.Count;

            public override bool AllowMultiSpawn => _allowMultiSpawn;

            public override float AutoReleaseInterval 
            { 
                get => _autoReleaseInterval;
                set => _autoReleaseInterval = value; 
            }

            public override int Capacity 
            { 
                get => _capacity;
                set
                {
                    if (value < 0)
                        throw new InvalidCastException("Capacity is InVaild");

                    if (_capacity == value)
                        return;

                    _capacity = value;
                    Release();
                }
            }

            public override float ExpireTime 
            { 
                get => _expireTime; 
                set
                {
                    if(value < 0f)
                    {
                        throw new InvalidCastException("ExpireTime is Invaild");
                    }

                    if (ExpireTime == value)
                        return;

                    _expireTime = value;
                    Release();
                }
            }

            /// <summary>
            /// 注册对象
            /// </summary>
            /// <param name="obj"></param>
            /// <param name="spawned"></param>
            public void Register(T obj,bool spawned)
            {
                if (obj == null)
                    throw new NullReferenceException("obj is null");

                //string name = obj.Name;
                Object<T> internalObject = Object<T>.Create(obj, spawned);

                if (!_objects.ContainsKey(obj.Name))
                {
                    _objects.Add(obj.Name, new List<Object<T>>());
                }

                _objects[obj.Name].Add(internalObject);
                _objectMap.Add(obj.Target, internalObject);

                if (Count > _capacity)
                    Release();
            }

            public bool CanSpawn(string name)
            {
                if (name == null)
                    throw new NullReferenceException("name is null");

                if(_objects.TryGetValue(name,out var queue))
                {
                    foreach(var internalObject in queue)
                    {
                        if (internalObject.IsInUse)
                            return true;
                    }
                }

                return false;
            }

            public T Spawn(string name)
            {
                if (string.IsNullOrEmpty(name))
                    throw new NullReferenceException("name is null");

                if (_objects.TryGetValue(name, out var list))
                {
                    foreach (var internalObject in list)
                    {
                        if (!internalObject.IsInUse)
                            return internalObject.Spawn();
                    }
                }

                return null;
            }

            /// <summary>
            /// 回收
            /// </summary>
            public void Unspawn(T obj)
            {
                if (obj == null)
                    throw new Exception();

                Unspawn(obj.Target);
            }

            /// <summary>
            /// 回收
            /// </summary>
            private void Unspawn(object target)
            {
                if (target == null)
                    throw new Exception();

                Object<T> internalObject = GetObject(target);
                if (internalObject == null)
                    throw new Exception();

                internalObject.UnSpawn();
                if (Count > _capacity && internalObject.SpawnCount > 0)
                    Release();
            }

            /// <summary>
            /// 释放对象
            /// </summary>
            /// <param name="obj"></param>
            /// <returns></returns>
            /// <exception cref="Exception"></exception>
            public bool ReleaseObject(T obj)
            {
                if (obj == null)
                    throw new Exception();
                return ReleaseObject(obj.Target);
            }

            /// <summary>
            /// 释放对象,修改为私有，等用到之后再改成公有
            /// </summary>
            private bool ReleaseObject(object target)
            {
                if (target == null)
                    throw new Exception();

                Object<T> internalObject = GetObject(target);
                if (internalObject == null)
                    return false;

                if (internalObject.IsInUse)
                    return false;

                //map是肯定要移除，它们是一对一的关系
                _objectMap.Remove(internalObject.Peek().Target);
                _objects[internalObject.Name].Remove(internalObject);

                internalObject.Release(false);
                ReferencePool.Recycle(internalObject);
                return true;
            }

            private Object<T> GetObject(object target)
            {
                if (target == null)
                {
                    throw new InvalidCastException("Target is invalid.");
                }

                if (_objectMap.TryGetValue(target, out var internalObject))
                {
                    return internalObject;
                }

                return null;
            }

            public override void Release()
            {
                
            }

            public override void Release(int toReleaseCount)
            {
                
            }

            public override void ReleaseAllUnused()
            {
                
            }

            internal override void Shutdown()
            {
                foreach(var objectInMap in _objectMap)
                {
                    objectInMap.Value.Release(true);
                    ReferencePool.Recycle(objectInMap.Value);
                }

                _objects.Clear();
                _objectMap.Clear();
                _canReleaseObjects.Clear();
                _toReleaseObjects.Clear();
            }

            internal override void Update(float elapseSeconds, float realElapseSeconds)
            {
                //throw new NotImplementedException();
            }
        }
    }
}

